<template>
  <a-form-item
    v-if="element"
    :key="element.model"
    class="generate-item"
    :style="{ marginBottom: isShow ? 0 : '16px' }"
    :colon="false"
    :name="element.model"
  >
    <template v-if="element.type !== 'text'" #label>
      {{ !element.options.showLabel ? element.fieldName : '' }}
      <a-tooltip v-if="element.options.hidePromptFlag && !element.options.showLabel">
        <template #title>{{ element.options.promptInfo }}</template>
        <i
          class="wentishuoming icpIconfont hover-primary cursor-pointer"
          style="font-size: 20px; line-height: normal"
        ></i>
      </a-tooltip>
    </template>
    <template v-if="element.type === 'input'">
      <template v-if="isShow">
        {{ data || '--' }}
      </template>
      <template v-else>
        <a-input
          v-model:value.trim="data"
          :size="config.size"
          :style="{ width: element.options.width }"
          :placeholder="element.options.placeholder"
          :maxlength="parseInt(element.options.maxlength)"
          :prefix="element.options.prefix"
          :suffix="element.options.suffix"
          :addon-before="element.options.addonBefore"
          :addon-after="element.options.addonAfter"
          :allow-clear="element.options.allowClear"
          :disabled="disabled || element.options.readonlyFlag === 1"
          @change="inputChange"
        />
      </template>
    </template>

    <template v-if="element.type === 'password'">
      <a-input-password
        v-model:value="data"
        :size="config.size"
        :style="{ width: element.options.width }"
        :placeholder="element.options.placeholder"
        :maxlength="element.options.maxlength"
        :prefix="element.options.prefix"
        :suffix="element.options.suffix"
        :addon-before="element.options.addonBefore"
        :addon-after="element.options.addonAfter"
        :allow-clear="element.options.allowClear"
        :disabled="disabled || element.options.readonlyFlag === 1"
        :visibility-toggle="element.options.visibilityToggle"
      />
    </template>

    <template v-if="element.type === 'textarea'">
      <template v-if="isShow">
        {{ data || '--' }}
      </template>
      <template v-else>
        <a-textarea
          v-model:value="data"
          style="resize: none"
          :size="config.size"
          :rows="element.options.rows"
          :style="{ width: element.options.width }"
          :placeholder="element.options.placeholder"
          :maxlength="element.options.maxlength"
          :show-count="element.options.showCount"
          :auto-size="element.options.autoSize"
          :allow-clear="element.options.allowClear"
          :disabled="disabled || element.options.readonlyFlag === 1"
        />
      </template>
    </template>

    <template v-if="element.type === 'number'">
      <template v-if="isShow">
        {{
          element.options.numericFormat == 1
            ? (data || '--') + ''
            : `￥ ${data || '--'}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
        }}
      </template>
      <template v-else>
        <a-input-number
          v-model:value="data"
          :size="config.size"
          :style="{ width: element.options.width }"
          :controls="false"
          decimal-separator="2"
          :disabled="disabled || element.options.readonlyFlag === 1"
          :placeholder="element.options.placeholder"
          :formatter="
            (value) =>
              element.options.numericFormat == 1 ? value + '' : `￥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
          "
          :parser="(value) => (element.options.numericFormat == 1 ? value : value.replace(/\￥\s?|(,*)/g, ''))"
          @change="numberChange"
        />
      </template>
    </template>

    <template v-if="element.type === 'radio'">
      <template v-if="isShow">
        {{ element.options.valueContextObj?.optionVal?.label || '--' }}
      </template>
      <template v-else>
        <a-radio-group
          v-model:value="data"
          :size="config.size"
          :style="{ width: element.options.width }"
          :disabled="disabled || element.options.readonlyFlag === 1"
          @change="radioOptionChange"
        >
          <a-radio
            v-for="item of element.options.remote === 2 ? element.options.remoteOptions : element.options.options"
            :key="item.value"
            :value="item.value"
            :style="{
              display: element.options.inline ? 'inline-block' : 'block'
            }"
            >{{ item.label }}</a-radio
          >
        </a-radio-group>
      </template>
    </template>

    <template v-if="element.type === 'checkbox'">
      <template v-if="isShow">
        {{
          element.options.valueContextObj.optionsVal
            ? element.options.valueContextObj.optionsVal.map((item) => item.fullLabel || item.label).join('、')
            : '--'
        }}
      </template>
      <template v-else>
        <a-checkbox-group
          v-model:value="data"
          :style="{ width: element.options.width }"
          :disabled="disabled || element.options.readonlyFlag === 1"
          @change="checkBoxChange"
        >
          <a-checkbox
            v-for="item of element.options.remote === 2 ? element.options.remoteOptions : element.options.options"
            :key="item.value"
            :value="item.value"
            :style="{
              display: element.options.inline ? '' : 'block'
            }"
            >{{ item.label }}</a-checkbox
          >
        </a-checkbox-group>
      </template>
    </template>

    <template v-if="element.type === 'time'">
      <a-time-picker
        v-model:value="data"
        :size="config.size"
        :placeholder="element.options.placeholder"
        :input-read-only="element.options.readonlyFlag === 1"
        :allow-clear="element.options.allowClear"
        :format="element.options.format"
        :value-format="element.options.valueFormat"
        :disabled="disabled || element.options.readonlyFlag === 1"
        :style="{ width: element.options.width }"
        :get-popup-container="getPopupContainer"
      />
    </template>

    <template v-if="element.type === 'date'">
      <template v-if="isShow">
        {{ element.options.valueContextObj?.stringVal || '--' }}
      </template>
      <template v-else>
        <a-date-picker
          v-model:value="data"
          :size="config.size"
          :placeholder="element.options.placeholder"
          :input-read-only="element.options.readonlyFlag === 1"
          :allow-clear="element.options.allowClear"
          :picker="getDatePickerType(element.options.dateType)"
          :format="getDatePickerFormat(element.options.dateType, element.options.dateFormat).label"
          :value-format="getDatePickerFormat(element.options.dateType, element.options.dateFormat).label"
          :show-time="getDatePickerShowTime(element.options.dateType)"
          :disabled="disabled || element.options.readonlyFlag === 1"
          :style="{ width: element.options.width }"
          :get-popup-container="getPopupContainer"
        />
      </template>
    </template>

    <template v-if="element.type === 'rate'">
      <a-rate
        v-model:value="data"
        :count="element.options.max"
        :allow-half="element.options.allowHalf"
        :allow-clear="element.options.allowClear"
        :disabled="disabled || element.options.disabled"
      />
    </template>

    <template v-if="element.type === 'select'">
      <template v-if="element.options.multipleFlag">
        <template v-if="isShow">
          {{
            element.options.valueContextObj?.optionsVal
              ? element.options.valueContextObj.optionsVal.map((item) => item.fullLabel || item.label).join('、')
              : '--'
          }}
        </template>
        <template v-else>
          <a-select
            v-model:value="data"
            :size="config.size"
            mode="multiple"
            :placeholder="element.options.placeholder"
            :filter-option="handleFilterOption"
            :allow-clear="element.options.clearable"
            :disabled="disabled || element.options.readonlyFlag === 1"
            :style="{ width: element.options.width }"
            :not-found-content="element.options.notFoundContent"
            :get-popup-container="getPopupContainer"
            @change="selectOptionChange"
          >
            <template v-if="element.options.remote === 2">
              <a-select-option
                v-for="item of element.options.remoteOptions"
                :key="item.value"
                :value="item.value"
                :label="item.fullLabel"
              >
                {{ item.fullLabel }}
              </a-select-option>
            </template>
            <template v-else>
              <a-select-option
                v-for="item of element.options.options"
                :key="item.value"
                :value="item.value"
                :label="item.label"
              >
                {{ item.label }}
              </a-select-option>
            </template>
          </a-select>
        </template>
      </template>
      <template v-else>
        <template v-if="isShow">
          {{
            element.options.valueContextObj?.optionsVal?.[0].fullLabel ||
            element.options.valueContextObj?.optionsVal?.[0].label ||
            '--'
          }}
        </template>
        <template v-else>
          <a-select
            v-model:value="data"
            :size="config.size"
            :placeholder="element.options.placeholder"
            :filter-option="element.options.remote === 2 ? false : handleFilterOption"
            :allow-clear="element.options.clearable"
            show-search
            :disabled="disabled || element.options.readonlyFlag === 1"
            :style="{ width: element.options.width }"
            :not-found-content="element.options.notFoundContent"
            :get-popup-container="getPopupContainer"
            @search="handleSearch"
            @change="selectOptionChange"
          >
            <template v-if="element.options.remote === 2">
              <a-select-option
                v-for="item of element.options.remoteOptions"
                :key="item.value"
                :value="item.value"
                :label="item.fullLabel"
              >
                {{ item.fullLabel }}
              </a-select-option>
            </template>
            <template v-else>
              <a-select-option
                v-for="item of element.options.options"
                :key="item.value"
                :value="item.value"
                :label="item.label"
              >
                {{ item.label }}
              </a-select-option>
            </template>
          </a-select>
        </template>
      </template>
    </template>

    <template v-if="element.type === 'switch'">
      <a-switch
        v-model:checked="data"
        :size="config.size === 'large' ? 'default' : config.size"
        :checked-children="element.options.checkedChildren"
        :un-checked-children="element.options.unCheckedChildren"
        :disabled="disabled || element.options.disabled"
      />
    </template>

    <template v-if="element.type === 'slider'">
      <a-slider
        v-model:value="data"
        :min="element.options.min"
        :max="element.options.max"
        :step="element.options.step"
        :range="element.options.range"
        :reverse="element.options.reverse"
        :disabled="disabled || element.options.disabled"
        :style="{ width: element.options.width }"
      />
    </template>

    <template v-if="element.type === 'text'">
      <h1>
        <span v-if="!element.options.showLabel">{{ element.fieldName }}</span>
        <a-tooltip v-if="element.options.hidePromptFlag">
          <template #title>{{ element.options.promptInfo }}</template>
          <i
            class="wentishuoming icpIconfont hover-primary cursor-pointer"
            style="font-size: 20px; line-height: normal"
          ></i>
        </a-tooltip>
      </h1>
      <span v-if="!element.options.hidePromptFlag" class="filed-desc">{{ element.options.promptInfo }}</span>
      <a-divider v-if="element.options.showDivider" style="margin: 0" />
    </template>

    <template v-if="element.type === 'upload'">
      <UploadFile
        v-model:fileList="data"
        :limit="element.options.attachmentNumber"
        :readonly="disabled || element.options.disabled"
        :show-file-list="true"
        :remote-replace="remoteReplace"
      />
    </template>

    <template v-if="element.type === 'img-upload'">
      <a-upload
        :name="element.options.file"
        :action="element.options.action"
        :accept="element.options.accept"
        :file-list="data"
        :list-type="element.options.listType"
        :multiple="element.options.multiple"
        :disabled="disabled || element.options.disabled"
        @change="handleUploadChange"
      >
        <SvgIcon v-if="element.options.listType === 'picture-card'" name="insert" prefix="icon-form" />
        <a-button v-else>
          <SvgIcon name="img-upload" prefix="icon-form" style="margin-right: 10px" />
          点击上传
        </a-button>
      </a-upload>
    </template>

    <template v-if="element.type === 'richtext-editor'">
      <!-- <RichTextEditor
        v-model:value="data"
        :disable="disabled || element.options.disabled"
        :style="{ width: element.options.width }"
      /> -->
    </template>

    <template v-if="element.type === 'cascader'">
      <a-cascader
        v-model:value="data"
        :size="config.size"
        :options="element.options.remoteOptions"
        :placeholder="element.options.placeholder"
        :allow-clear="element.options.allowClear"
        :disabled="disabled || element.options.readonlyFlag === 1"
        expand-trigger="hover"
        :style="{ width: element.options.width }"
        @change="changeCascaderOption"
      />
    </template>
    <template v-if="element.type === 'treeSelect'">
      <template v-if="isShow">
        {{
          element.options.valueContextObj?.optionsVal?.[0].fullLabel ||
          element.options.valueContextObj?.optionsVal?.[0].label ||
          '--'
        }}
      </template>
      <TreeSelectInput
        v-else
        v-model:value="data"
        :size="config.size"
        :is-https="false"
        :field-names="{
          children: element.options.remoteConfig.children,
          label: element.options.remoteConfig.labelKey,
          value: element.options.remoteConfig.valueKey
        }"
        :is-ready="isReady"
        :data-list="element.options.remoteOptions"
        :disabled="disabled || element.options.readonlyFlag === 1"
        :style="{ width: element.options.width }"
        dropdown-class-name="tree-select-custom-width minw-300"
        :placeholder="element.options.placeholder"
        @change="changeTreeSelect"
      ></TreeSelectInput>
    </template>
    <p
      v-if="element.type !== 'text' && !element.options.hidePromptFlag && element.options.promptInfo"
      class="filed-desc"
    >
      {{ element.options.promptInfo }}
    </p>
  </a-form-item>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue'
// import SvgIcon from '@/components/SvgIcon.vue'
// import RichTextEditor from '@/components/RichTextEditor.vue'
import UploadFile from '@@/components/uploadAndPreview/UploadFile.vue'
import { WidgetForm } from './config/antd'
import { http } from '@@/utils/axios/init'
import { getDatePickerType, getDatePickerFormat, getDatePickerShowTime, getValueByModel } from './config'
import { isProjectInner } from '@@/utils/is/index'
import { debounce } from 'lodash-es'

export default defineComponent({
  name: 'AntdGenerateFormItem',
  components: {
    UploadFile
    // SvgIcon,
    // RichTextEditor
  },
  props: {
    config: {
      type: Object as PropType<WidgetForm['config']>,
      required: true
    },
    element: {
      type: Object,
      required: true
    },
    model: {
      type: Object,
      required: true
    },
    disabled: {
      type: Boolean,
      required: true
    },
    isShow: {
      type: Boolean,
      required: false
    },
    remoteReplace: {
      type: Object,
      required: true
    }
  },
  emits: {
    change: null
  },
  setup(props, context) {
    const getPopupContainer: any = (targetElement) => {
      return targetElement.closest('.ant-form')
    }
    const data = computed({
      get: () => getValueByModel(props.model[props.element.model], props.element),
      set: (val) => {
        if (props.element.type !== 'select') {
          // eslint-disable-next-line vue/no-mutating-props
          props.model[props.element.model] = val
          return
        }
        if (props.element.options.multipleFlag) {
          const optionArr =
            props.element.options.remote === 1 ? props.element.options.options : props.element.options.remoteOptions
          const tempArr: any = []
          for (const optVal of val) {
            const option = optionArr.find((opt) => opt.value === optVal)
            if (option) {
              tempArr.push({ value: option.value, label: option.label, fullLabel: option.fullLabel })
            }
          }
          // eslint-disable-next-line vue/no-mutating-props
          props.model[props.element.model] = tempArr
        } else {
          const optionArr =
            props.element.options.remote === 1 ? props.element.options.options : props.element.options.remoteOptions
          const tempObj: any = {}
          const option = optionArr.find((opt) => opt.value === val)
          if (option) {
            tempObj.value = option.value
            tempObj.label = option.label
            tempObj.fullLabel = option.fullLabel
          }
          // eslint-disable-next-line vue/no-mutating-props
          props.model[props.element.model] = tempObj
        }
      }
    })

    const handleFilterOption = (input: string, option: { label: string }) => option.label.toLowerCase().includes(input)

    const handleUploadChange = ({ fileList }: any) => {
      // 文件上传组件未对接
      data.value = fileList
    }

    /**
     * 选中选项的展示值（单选）
     */
    const selectedLabel = ref('')
    const selectedId = ref('')
    const isReady = ref(false)
    const setLabelAndUpdateOpt = (val?) => {
      const item = props.element
      if (item.options.remote !== 2) {
        // 非远端数据源
        return
      }
      if (val === undefined || val === null || val === '') {
        // 未输入搜索时，优先从读取选中选项的label
        selectedLabel.value = props.model[props.element.model]?.label || ''
      } else {
        selectedLabel.value = val
      }
      selectedId.value = props.model[props.element.model]?.value || ''
      getRemoteData()
    }

    setLabelAndUpdateOpt()

    // 远端搜索
    const handleSearch = debounce((val) => {
      setLabelAndUpdateOpt(val)
    }, 500)

    /**
     * 查询远端数据
     */
    function getRemoteData() {
      if (props.isShow) {
        // 表单整体只读，不需要查询数据
        return
      }
      const item = props.element
      if (item.options.remote !== 2) {
        // 非远端数据源
        return
      }
      if (!item.options.remoteConfig.url) {
        // 未配置远端请求地址
        item.options.remoteOptions = []
        return
      }
      if (
        !props.remoteReplace.projectId &&
        !isProjectInner() &&
        item.options.remoteConfig.url.includes('${projectId}')
      ) {
        // 未指明项目Id且未在项目中预览，无法获取项目Id
        item.options.remoteOptions = []
        item.options.notFoundContent = '预览时暂无数据，请在具体项目中使用时进行选择'
        return
      }
      const url = item.options.remoteConfig.url
        .replace(/\$\{orgId\}/, props.remoteReplace.orgId || -1)
        .replace(/\$\{projectId\}/, props.remoteReplace.projectId || 0)
        .replace(/\$\{searchVal\}/, selectedLabel.value)
        .replace(/\$\{searchId\}/, selectedId.value)
      http
        .request({
          url: url,
          method: 'get'
        })
        .then((json) => {
          if (item.type === 'cascader') {
            item.options.remoteOptions = json.map((data) => ({
              label: data[item.options.remoteConfig.labelKey],
              value: data[item.options.remoteConfig.valueKey],
              children: data[item.options.remoteConfig.children]
            }))
            return
          }
          if (item.options.multipleFlag) {
            item.options.remoteOptions = convertSelectRemoteOptions(json)
            return
          }
          if (json.list && json.list instanceof Array) {
            item.options.remoteOptions = convertSelectRemoteOptions(json.list)
          }
          if (item.type === 'treeSelect') {
            item.options.remoteOptions = json
            isReady.value = true
          }
        })
    }

    /**
     * 转换选择框的远端选项
     */
    const convertSelectRemoteOptions = (remoteArr) => {
      const remoteConfig = props.element.options.remoteConfig
      const labelStr = remoteConfig.fullLabel
      if (labelStr) {
        const keyArr = parseLableKey(labelStr)
        const arr: any[] = []
        for (const tempItem of remoteArr) {
          let fullLabel = labelStr
          for (const key of keyArr) {
            fullLabel = fullLabel.replace('${' + key + '}', tempItem[key] || '')
          }
          arr.push({
            label: tempItem[remoteConfig.labelKey],
            value: tempItem[remoteConfig.valueKey],
            fullLabel
          })
        }
        return arr
      }
      return remoteArr.map((data) => ({
        label: data[remoteConfig.labelKey],
        value: data[remoteConfig.valueKey],
        fullLabel: data[remoteConfig.labelKey]
      }))
    }

    /**
     * 解析远端配置中的标签
     */
    const parseLableKey = (str) => {
      const reg = /\$\{(\w*)\}/g
      let temp: any = []
      const keyArr: any[] = []
      while ((temp = reg.exec(str))) {
        keyArr.push(temp[1])
      }
      return keyArr
    }

    const radioOptionChange = () => {
      context.emit('change', { element: props.element, value: data.value })
    }
    const checkBoxChange = () => {
      context.emit('change', { element: props.element, value: data.value })
    }

    const selectOptionChange = (val) => {
      if (!props.element.options.multipleFlag) {
        setLabelAndUpdateOpt()
      }
      context.emit('change', { element: props.element, value: val })
    }

    const changeCascaderOption = (value) => {
      context.emit('change', { element: props.element, value })
    }
    const changeTreeSelect = (value) => {
      context.emit('change', { element: props.element, value: value.id })
    }

    const inputChange = () => {
      context.emit('change', {
        element: props.element,
        value: getValueByModel(props.model[props.element.model], props.element)
      })
    }

    const numberChange = (value: number | string) => {
      context.emit('change', { element: props.element, value })
    }

    return {
      data,
      isReady,
      handleFilterOption,
      handleUploadChange,
      radioOptionChange,
      checkBoxChange,
      selectOptionChange,
      handleSearch,
      getDatePickerType,
      getDatePickerFormat,
      getDatePickerShowTime,
      changeCascaderOption,
      changeTreeSelect,
      inputChange,
      numberChange,
      getPopupContainer
    }
  }
})
</script>
<style lang="less" scoped>
.generate-item {
  h1 {
    font-size: 16px;
    font-weight: bold;
  }
}
</style>
